# 五子棋游戏 HTML 实现

以下是一个完整的五子棋游戏 HTML 实现，包含人类玩家（黑子）和电脑 AI（白子）对战功能，电脑 AI 会尽力防守但不会主动取胜，游戏结束后会显示双方评分并记录历史得分。

```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>五子棋游戏</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            display: flex;
            flex-direction: column;
            align-items: center;
            background-color: #f5f5f5;
            padding: 20px;
        }
        h1 {
            color: #333;
            margin-bottom: 10px;
        }
        .game-container {
            display: flex;
            gap: 30px;
            margin-top: 20px;
        }
        .board-container {
            position: relative;
        }
        #board {
            background-color: #dcb35c;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
        }
        .info-panel {
            width: 250px;
            background-color: white;
            padding: 15px;
            border-radius: 5px;
            box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
        }
        .current-player {
            font-size: 18px;
            margin-bottom: 15px;
            padding: 10px;
            text-align: center;
            border-radius: 5px;
        }
        .black {
            background-color: #000;
            color: white;
        }
        .white {
            background-color: #fff;
            color: #333;
            border: 1px solid #ccc;
        }
        .score-history {
            margin-top: 20px;
            max-height: 300px;
            overflow-y: auto;
        }
        .score-item {
            padding: 8px;
            margin-bottom: 5px;
            border-radius: 3px;
            background-color: #f9f9f9;
        }
        .winner {
            color: green;
            font-weight: bold;
        }
        .loser {
            color: red;
        }
        .restart-btn {
            margin-top: 15px;
            padding: 10px 15px;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
        }
        .restart-btn:hover {
            background-color: #45a049;
        }
        .game-status {
            margin-top: 15px;
            font-size: 18px;
            font-weight: bold;
            text-align: center;
            min-height: 27px;
        }
    </style>
</head>
<body>
    <h1>五子棋游戏</h1>
    <p>人类(黑子) vs 电脑(白子) - 人类先行</p>
    
    <div class="game-container">
        <div class="board-container">
            <canvas id="board" width="450" height="450"></canvas>
        </div>
        
        <div class="info-panel">
            <div id="current-player" class="current-player black">当前回合: 人类(黑子)</div>
            <div id="game-status" class="game-status"></div>
            
            <div class="score-history">
                <h3>得分记录</h3>
                <div id="score-records"></div>
            </div>
            
            <button id="restart-btn" class="restart-btn">重新开始</button>
        </div>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            const canvas = document.getElementById('board');
            const ctx = canvas.getContext('2d');
            const currentPlayerDisplay = document.getElementById('current-player');
            const gameStatusDisplay = document.getElementById('game-status');
            const scoreRecordsContainer = document.getElementById('score-records');
            const restartBtn = document.getElementById('restart-btn');
            
            const BOARD_SIZE = 15;
            const CELL_SIZE = 30;
            const PIECE_RADIUS = 13;
            
            let board = Array(BOARD_SIZE).fill().map(() => Array(BOARD_SIZE).fill(0));
            let currentPlayer = 1; // 1 for human (black), 2 for computer (white)
            let gameOver = false;
            let scoreRecords = [];
            
            // Initialize the board
            function initBoard() {
                // Draw the grid
                ctx.fillStyle = '#dcb35c';
                ctx.fillRect(0, 0, canvas.width, canvas.height);
                
                ctx.strokeStyle = '#000';
                ctx.lineWidth = 1;
                
                for (let i = 0; i < BOARD_SIZE; i++) {
                    // Horizontal lines
                    ctx.beginPath();
                    ctx.moveTo(CELL_SIZE / 2, i * CELL_SIZE + CELL_SIZE / 2);
                    ctx.lineTo(canvas.width - CELL_SIZE / 2, i * CELL_SIZE + CELL_SIZE / 2);
                    ctx.stroke();
                    
                    // Vertical lines
                    ctx.beginPath();
                    ctx.moveTo(i * CELL_SIZE + CELL_SIZE / 2, CELL_SIZE / 2);
                    ctx.lineTo(i * CELL_SIZE + CELL_SIZE / 2, canvas.height - CELL_SIZE / 2);
                    ctx.stroke();
                }
                
                // Draw star points
                const starPoints = [3, 7, 11];
                ctx.fillStyle = '#000';
                
                for (let i of starPoints) {
                    for (let j of starPoints) {
                        ctx.beginPath();
                        ctx.arc(
                            i * CELL_SIZE + CELL_SIZE / 2,
                            j * CELL_SIZE + CELL_SIZE / 2,
                            3,
                            0,
                            Math.PI * 2
                        );
                        ctx.fill();
                    }
                }
            }
            
            // Draw a piece on the board
            function drawPiece(row, col, player) {
                const x = col * CELL_SIZE + CELL_SIZE / 2;
                const y = row * CELL_SIZE + CELL_SIZE / 2;
                
                ctx.beginPath();
                ctx.arc(x, y, PIECE_RADIUS, 0, Math.PI * 2);
                
                if (player === 1) {
                    // Black piece
                    ctx.fillStyle = '#000';
                    ctx.fill();
                } else {
                    // White piece
                    ctx.fillStyle = '#fff';
                    ctx.fill();
                    ctx.strokeStyle = '#000';
                    ctx.lineWidth = 1;
                    ctx.stroke();
                }
            }
            
            // Check if a move is valid
            function isValidMove(row, col) {
                return row >= 0 && row < BOARD_SIZE && 
                       col >= 0 && col < BOARD_SIZE && 
                       board[row][col] === 0;
            }
            
            // Make a move
            function makeMove(row, col, player) {
                if (gameOver || !isValidMove(row, col)) return false;
                
                board[row][col] = player;
                drawPiece(row, col, player);
                return true;
            }
            
            // Check if the game is won
            function checkWin(row, col, player) {
                const directions = [
                    [0, 1],   // horizontal
                    [1, 0],   // vertical
                    [1, 1],   // diagonal down-right
                    [1, -1]   // diagonal down-left
                ];
                
                for (const [dx, dy] of directions) {
                    let count = 1;
                    
                    // Check in positive direction
                    for (let i = 1; i < 5; i++) {
                        const r = row + i * dx;
                        const c = col + i * dy;
                        if (r < 0 || r >= BOARD_SIZE || c < 0 || c >= BOARD_SIZE || board[r][c] !== player) break;
                        count++;
                    }
                    
                    // Check in negative direction
                    for (let i = 1; i < 5; i++) {
                        const r = row - i * dx;
                        const c = col - i * dy;
                        if (r < 0 || r >= BOARD_SIZE || c < 0 || c >= BOARD_SIZE || board[r][c] !== player) break;
                        count++;
                    }
                    
                    if (count >= 5) return true;
                }
                
                return false;
            }
            
            // Check if the board is full
            function isBoardFull() {
                for (let row = 0; row < BOARD_SIZE; row++) {
                    for (let col = 0; col < BOARD_SIZE; col++) {
                        if (board[row][col] === 0) return false;
                    }
                }
                return true;
            }
            
            // Computer AI move
            function computerMove() {
                if (gameOver) return;
                
                // Simple AI that tries to block human and create its own opportunities
                const bestMove = findBestMove();
                
                setTimeout(() => {
                    if (makeMove(bestMove.row, bestMove.col, 2)) {
                        if (checkWin(bestMove.row, bestMove.col, 2)) {
                            // Computer should never win, so we undo this move and force a draw
                            board[bestMove.row][bestMove.col] = 0;
                            redrawBoard();
                            endGame(0); // Draw
                            return;
                        }
                        
                        currentPlayer = 1;
                        updateCurrentPlayerDisplay();
                        
                        if (isBoardFull()) {
                            endGame(0); // Draw
                        }
                    }
                }, 500);
            }
            
            // Find the best move for the computer
            function findBestMove() {
                // First, check if computer can win (but we'll prevent it from actually winning)
                for (let row = 0; row < BOARD_SIZE; row++) {
                    for (let col = 0; col < BOARD_SIZE; col++) {
                        if (board[row][col] === 0) {
                            board[row][col] = 2;
                            if (checkWin(row, col, 2)) {
                                board[row][col] = 0; // Undo the test move
                                return { row, col, score: 10000 }; // But we'll prevent the win later
                            }
                            board[row][col] = 0;
                        }
                    }
                }
                
                // Check if human is about to win and block
                for (let row = 0; row < BOARD_SIZE; row++) {
                    for (let col = 0; col < BOARD_SIZE; col++) {
                        if (board[row][col] === 0) {
                            board[row][col] = 1;
                            if (checkWin(row, col, 1)) {
                                board[row][col] = 0;
                                return { row, col, score: 9000 };
                            }
                            board[row][col] = 0;
                        }
                    }
                }
                
                // Evaluate all possible moves
                let bestScore = -Infinity;
                let bestMoves = [];
                
                for (let row = 0; row < BOARD_SIZE; row++) {
                    for (let col = 0; col < BOARD_SIZE; col++) {
                        if (board[row][col] === 0) {
                            const score = evaluateMove(row, col);
                            if (score > bestScore) {
                                bestScore = score;
                                bestMoves = [{ row, col, score }];
                            } else if (score === bestScore) {
                                bestMoves.push({ row, col, score });
                            }
                        }
                    }
                }
                
                // Randomly select among the best moves
                return bestMoves[Math.floor(Math.random() * bestMoves.length)];
            }
            
            // Evaluate a potential move
            function evaluateMove(row, col) {
                let score = 0;
                
                // Center control
                const centerDist = Math.sqrt(
                    Math.pow(row - BOARD_SIZE/2, 2) + 
                    Math.pow(col - BOARD_SIZE/2, 2)
                );
                score += (BOARD_SIZE - centerDist) * 2;
                
                // Check for potential lines
                score += evaluateDirection(row, col, 0, 1);   // horizontal
                score += evaluateDirection(row, col, 1, 0);   // vertical
                score += evaluateDirection(row, col, 1, 1);   // diagonal
                score += evaluateDirection(row, col, 1, -1);  // anti-diagonal
                
                return score;
            }
            
            // Evaluate a direction for potential lines
            function evaluateDirection(row, col, dx, dy) {
                let score = 0;
                let computerCount = 0;
                let humanCount = 0;
                let emptyCount = 0;
                
                // Evaluate in both directions
                for (let i = -4; i <= 4; i++) {
                    if (i === 0) continue; // Skip the current position
                    
                    const r = row + i * dx;
                    const c = col + i * dy;
                    
                    if (r < 0 || r >= BOARD_SIZE || c < 0 || c >= BOARD_SIZE) continue;
                    
                    if (board[r][c] === 2) computerCount++;
                    else if (board[r][c] === 1) humanCount++;
                    else emptyCount++;
                }
                
                // Score based on potential
                if (computerCount >= 3) score += 100;
                else if (computerCount >= 2) score += 50;
                else if (computerCount >= 1) score += 10;
                
                if (humanCount >= 3) score += 80;
                else if (humanCount >= 2) score += 30;
                else if (humanCount >= 1) score += 5;
                
                return score;
            }
            
            // End the game
            function endGame(result) {
                gameOver = true;
                
                let humanScore = 0;
                let computerScore = 0;
                
                // Calculate scores based on the result
                if (result === 1) {
                    // Human wins
                    gameStatusDisplay.textContent = "游戏结束: 人类获胜!";
                    gameStatusDisplay.style.color = "green";
                    humanScore = 100;
                    computerScore = 50;
                } else if (result === 2) {
                    // Computer wins (shouldn't happen)
                    gameStatusDisplay.textContent = "游戏结束: 电脑获胜!";
                    gameStatusDisplay.style.color = "red";
                    humanScore = 0;
                    computerScore = 100;
                } else {
                    // Draw
                    gameStatusDisplay.textContent = "游戏结束: 平局!";
                    gameStatusDisplay.style.color = "blue";
                    humanScore = 75;
                    computerScore = 75;
                }
                
                // Add to score records
                const record = {
                    humanScore,
                    computerScore,
                    result,
                    date: new Date().toLocaleString()
                };
                
                scoreRecords.unshift(record);
                updateScoreRecords();
            }
            
            // Update the current player display
            function updateCurrentPlayerDisplay() {
                if (currentPlayer === 1) {
                    currentPlayerDisplay.textContent = "当前回合: 人类(黑子)";
                    currentPlayerDisplay.className = "current-player black";
                } else {
                    currentPlayerDisplay.textContent = "当前回合: 电脑(白子)";
                    currentPlayerDisplay.className = "current-player white";
                }
            }
            
            // Update the score records display
            function updateScoreRecords() {
                scoreRecordsContainer.innerHTML = '';
                
                if (scoreRecords.length === 0) {
                    scoreRecordsContainer.innerHTML = '<p>暂无记录</p>';
                    return;
                }
                
                scoreRecords.forEach((record, index) => {
                    const item = document.createElement('div');
                    item.className = 'score-item';
                    
                    const humanClass = record.result === 1 ? 'winner' : 
                                     record.result === 2 ? 'loser' : '';
                    const computerClass = record.result === 2 ? 'winner' : 
                                         record.result === 1 ? 'loser' : '';
                    
                    item.innerHTML = `
                        <div>对局 ${index + 1} - ${record.date}</div>
                        <div>人类: <span class="${humanClass}">${record.humanScore}</span></div>
                        <div>电脑: <span class="${computerClass}">${record.computerScore}</span></div>
                    `;
                    
                    scoreRecordsContainer.appendChild(item);
                });
            }
            
            // Redraw the entire board
            function redrawBoard() {
                initBoard();
                for (let row = 0; row < BOARD_SIZE; row++) {
                    for (let col = 0; col < BOARD_SIZE; col++) {
                        if (board[row][col] !== 0) {
                            drawPiece(row, col, board[row][col]);
                        }
                    }
                }
            }
            
            // Reset the game
            function resetGame() {
                board = Array(BOARD_SIZE).fill().map(() => Array(BOARD_SIZE).fill(0));
                currentPlayer = 1;
                gameOver = false;
                gameStatusDisplay.textContent = '';
                gameStatusDisplay.style.color = '';
                updateCurrentPlayerDisplay();
                initBoard();
            }
            
            // Handle canvas click
            canvas.addEventListener('click', function(e) {
                if (gameOver || currentPlayer !== 1) return;
                
                const rect = canvas.getBoundingClientRect();
                const x = e.clientX - rect.left;
                const y = e.clientY - rect.top;
                
                const col = Math.round((x - CELL_SIZE / 2) / CELL_SIZE);
                const row = Math.round((y - CELL_SIZE / 2) / CELL_SIZE);
                
                if (isValidMove(row, col)) {
                    if (makeMove(row, col, 1)) {
                        if (checkWin(row, col, 1)) {
                            endGame(1); // Human wins
                            return;
                        }
                        
                        if (isBoardFull()) {
                            endGame(0); // Draw
                            return;
                        }
                        
                        currentPlayer = 2;
                        updateCurrentPlayerDisplay();
                        computerMove();
                    }
                }
            });
            
            // Handle restart button
            restartBtn.addEventListener('click', resetGame);
            
            // Initialize the game
            resetGame();
        });
    </script>
</body>
</html>
```

## 游戏功能说明

1. **游戏规则**:
   - 人类玩家使用黑子